crackmeUK 解析手引き その4(DIFFICULT)


UK_crackme の DIFFICULT ということでいってみましょう。

OllyDBGを起動して、CrackMe UK を読み込んで下さい。
そのまま F9 キーで実行しましょう。CrackMe UK のウィンドウが表示されたかと思います。

フェイクパス "98765432" と入力して、 「検索→現在のモジュール名」から
GetWindowTextA ( Address 0041C430  USER32.GetWindowTextA) にブレークポイントを仕掛けてから
 OK ボタンを押すと 00417135 でブレークします。



0041712A  |. FF7424 08      push    dword ptr ss:[esp+8]             ; /Count
0041712E  |. FF7424 08      push    dword ptr ss:[esp+8]             ; |Buffer
00417132  |. FF71 1C        push    dword ptr ds:[ecx+1C]            ; |hWnd
00417135  |. FF15 30C44100  call    near dword ptr ds:[<&USER32.GetW>; \GetWindowTextA
0041713B  |. EB 12          jmp     short crackme_.0041714F


とりあえずブレークポイントを全解除して、適当に F8 を連打して ret 命令を抜けると
アドレス 00401C6F に出てくるはずです。
いきなりチェックルーチンが現れました。下のほうを見ると

00401CBA  |. 68 14224200    push    crackme_.00422214 ;  ASCII "Wonderful!! You analyzed powerful protection!!"

という文字列があります。


00401C7D  |. 8D5424 20      lea     edx, dword ptr ss:[esp+20]  ; edx = 入力パスのポインタ
00401C81  |. F2:AE          repne   scas byte ptr es:[edi]
00401C83  |. F7D1           not     ecx
00401C85  |. 49             dec     ecx


これは NORMAL でも解説したものと同じ文字数チェックです。


00401C86  |. 51             push    ecx                ; 入力パスの文字数
00401C87  |. 52             push    edx                ; edx = 入力パスのポインタ
00401C88  |. E8 53110000    call    crackme_.00402DE0  ; とても怪しい call


call 命令の前に入力パスの文字数、入力パスのポインタがプッシュされています。
非常に怪しいですね。そして、比較・分岐ルーチンです。


00401C96  |. 33D2           xor     edx, edx                    ; edx はループカウンタとして使用
00401C98  |. 8D4424 0C      lea     eax, dword ptr ss:[esp+C]   ; 怪しい値のポインタ
00401C9C  |. 8D8E D4000000  lea     ecx, dword ptr ds:[esi+D4]  ; 怪しい値のポインタ
00401CA2  |> 8B30           /mov     esi, dword ptr ds:[eax]    ; esi = 何やら怪しい値 <-+
00401CA4  |. 8B19           |mov     ebx, dword ptr ds:[ecx]    ; ebx = 何やら怪しい値   |
00401CA6  |. 33F7           |xor     esi, edi                   : edi = 固有 ID で xor   |
00401CA8  |. 3BDE           |cmp     ebx, esi                   ; 得た値を比較           |
00401CAA  |. 8930           |mov     dword ptr ds:[eax], esi    ;                        |
00401CAC  |. 75 22          |jnz     short crackme_.00401CD0    ; 値が違うとアウト       |
00401CAE  |. 42             |inc     edx                        ; ループカウンタ加算     |
00401CAF  |. 83C0 04        |add     eax, 4                     ; ポインタを進めている   |
00401CB2  |. 83C1 04        |add     ecx, 4                     ; ポインタを進めている   |
00401CB5  |. 83FA 04        |cmp     edx, 4                     ; 4回繰り返される       |
00401CB8  |.^7C E8          \jl      short crackme_.00401CA2  ---------------------------+
00401CBA  |. 68 14224200    push    crackme_.00422214  ;  ASCII "Wonderful!! You analyzed powerful protection!!"


右にコメントを入れてみました。最大4回ループしてチェックが行われるようです。
00401C98 〜 00401CA4 にある命令で怪しげな値が参照されていますが、ちょっと覗いてみましょう。

入力パスを変えて何度か試してみるとわかりますが、入力パスによって eax レジスタの指す値の方が変わることがわかります。
eax レジスタが指すアドレスに飛んで下さい。
(画面左下のダンプウィンドウにフォーカスを合わせ、 Ctrl + G → eax レジスタの値を入力)

0012F8D8  0F 6B C7 BA 3B 7E 74 EB 9C 26 11 DE 7B C9 67 F3  ................ <-- ここの値が参照される
0012F8E8  39 38 37 36 35 34 33 32 00 00 00 00 00 00 00 00  98765432........ <-- 入力パス

さて、ループは4回実行されるので、生成された値は 4byte * 4 の 128bit 分になります。
この生成された値を眺めて「ん?これって、MD5 じゃないのか?」と直感した訳ですが、
MD5 とは何か?とりあえず e-Words (http://e-words.jp/) から引いてみましょう。



MD5 (Message Digest 5) : エムディーファイブ

 認証やデジタル署名などに使われるハッシュ関数(一方向要約関数)のひとつ。 原文を元に固定長の「ハッシュ値」を発生し、通信経路の両端で比較することで、 通信途中で原文が改ざんされていないかを検出することができる。 計算方法には初期値敏感性の不可逆な一方向関数を含むため、ハッシュ値は擬似的な 乱数のような値をとり、これをもとに原文を再現することはできないようになっている。 また、同じハッシュ値を生成する別のメッセージを作成することも極めて困難である。 RSA暗号の開発者の一人、Ronald Rivest氏らによって開発された。 RFC 1321としてIETFで標準化されている。

つまり、逆算は不可能ということです(^^;。(アルゴリズムは公開されています) 逆算にかかる時間は正解パスの文字長に比例します。 CrackMeUK 発表直後の正解パスは 半角24文字(記号を含む)でした。 つまり、スパコンでも使うなりしないと無理、ということです。 ですから easy, normal は "攻略" と題しているのに difficult は "分析" ということになってました。 先程から解説が過去形な訳は、最初のパスの文字列は 半角 24 文字だったのですが、 あまりにも難しいので 数字・英文字(半角)で 3文字に後日修正されているからです。 24文字だった場合を想定した対処法としては、パッチで誤魔化しましょう。 キーチェックルーチンのループ内で、 00401CA8 |. 3BDE |cmp ebx, esi 00401CAA |. 8930 |mov dword ptr ds:[eax], esi 00401CAC |. 75 22 |jnz short crackme_.00401CD0 ; 不正解ならジャンプ こうありますが、 00401CA8 |. 3BDE |cmp ebx, esi00401CA8 3BDB cmp ebx, ebx と変更すると良さそうです。これでどんな値をいれても登録成功となるはずです。 確認の意味で実際に MD5 を使って確認してみましょう。MD5 のサンプルプログラムは そこら辺に落ちているので適当に拾ってみてください。 「Iczelion's Win32 Assembly Homepage」    http://spiff.tripnet.se/~iczelion/ 「DAMN」     http://www.damn.to/ も参考になるかと思います。 ここでは、Iczelion 大先生のページから落とせる asm で書かれた MD5 プログラムを使用されています。 http://spiff.tripnet.se/~iczelion/files/md5asm.zip  (直リン失礼) 【 UK_crackme で生成された値(入力テキスト "98765432")】 0012F8D8 0F 6B C7 BA 3B 7E 74 EB 9C 26 11 DE 7B C9 67 F3 ................ 【 サンプルプログラム md5 hasher で生成された値(入力テキスト "98765432")】 bac76b0feb747e3bde11269cf367c97b UK_crackme の方はリトルエンディアン表記なので各自読み替えて下さい。 同じ値ですね。ということで、MD5 が使用されていることが証明されました。 ここで MD5 が使用されているかを確認する別の方法をひとつ。 前述の通り、MD5 アルゴリズムは公開されています。そこで、MD5 アルゴリズムで 用いられている定数を検索することでルーチンを突き止めてみましょう。 まずブレークポイントは全解除してください。 「右クリック | 検索 | 定数」で、D76AA478 と値を打ち込んで検索してください。すると 00402EFC |. 8D8C01 78A46AD7 lea ecx, dword ptr ds:[ecx+eax+D76AA478] 一箇所だけ見つかりました。ここにブレークポイントを仕掛けます。 そしてキーチェック内部で呼び出されている怪しげな call の手前に ブレークポイントを仕掛けましょう。 00401C86 |. 51 push ecx 00401C87 |. 52 push edx ; ←このへんに BP を置く 00401C88 |. E8 53110000 call crackme_.00402DE0 ; 怪しい関数 00401C8D |. 8BBE D0000000 mov edi, dword ptr ds:[esi+D0] それでは "98765432" とフェイクパスを入力して OK ボタンを押してみましょう。 すると call 命令の手前でブレークしました。次に F8 を押して call 命令を 素通りしてみましょう。すると call 命令内部でブレークしました。 ということは MD5 が使用されていることでほぼ確定です。 ここまでの解説で解答の壮絶なヒントになっている気がする(笑) MD5 の解説はここまでにして本題に戻ります。 比較ループ部分で、入力パスから生成されたハッシュ値を固有 ID で xor しています。 もし固有 ID によって正当パスが変わるのであれば、逆算不可能な MD5 の性質からして 「作者が固有 ID から正当パスを発行することができない」ということになり、 「極めて非現実的なキーチェッカ」となってしまいます。 | 実は UK さんが本 crackme をテスト公開されていた時にこの事を報告したら | 「フェイクに引っかかってる〜」と言われました(^^; | 実はその報告をした後に固定パスだと気づいたのですが時すでに遅し(笑)。 ですが、固有 ID で xor してその値で比較を行っていることは事実です。 非現実的な方法はないものと考えると、固有 ID 取得時に正当パスから生成された ハッシュ値を生成していると思われます。先ほどの比較ループ部分で 00401C98 |. 8D4424 0C lea eax, dword ptr ss:[esp+C] ; 怪しい値のポインタ 00401C9C |. 8D8E D4000000 lea ecx, dword ptr ds:[esi+D4] ; 怪しい値のポインタ eax = 入力パスから生成されたハッシュ値のポインタ ecx = これが怪しい こんなのがありましたが、ecx レジスタが指すアドレスに比較対象となる値が入っています。 ここにブレークポイントを仕掛け、OK ボタンを押すとブレークするので、 この値(ecx レジスタの値)を覚えてから crackme を再スタートさせてください。 前述のように、固有 ID 取得時に正当パスから生成されたハッシュ値を生成していると 思われるので、GetVolumeInformationA (0040181D) にブレークポイントを仕掛け、 ダンプウィンドウにフォーカスを合わせ、 Ctrl + G → 先ほどの ecx レジスタの値を入力してください。 crackme を起動してから最初のメッセージを抜けるとブレークしました。 固有 ID 取得直後に比較したい値をセットしていると思われるので、ダンプウィンドウを 注視しながら F8 キーを叩き続けてください。 すると、 004018B7 . 8BCD mov ecx, ebp 004018B9 . E8 22040000 call crackme_.00401CE0 ; この call で値がセットされた 004018BE . 8D4C24 10 lea ecx, dword ptr ss:[esp+10] ; ここで値が変わる この部分でセットされました。それではブレークポイントを全解除、この部分 アドレス 004018B9 に ブレークポイントを仕掛けてから再スタートして call 内部をトレースしてみたいと思います。 00401CE0 /$ 8D81 D4000000 lea eax, dword ptr ds:[ecx+D4] 00401CE6 |. 56 push esi 00401CE7 |. 57 push edi 00401CE8 |. C781 D8000000 0E259B16 mov dword ptr ds:[ecx+D8], 169B250E ; 値セット 00401CF2 |. C700 098A137F mov dword ptr ds:[eax], 7F138A09 ; 値セット 00401CF8 |. C781 DC000000 8137CB9D mov dword ptr ds:[ecx+DC], 9DCB3781 ; 値セット 00401D02 |. C781 E0000000 78739040 mov dword ptr ds:[ecx+E0], 40907378 ; 値セット 00401D0C |. BA 04000000 mov edx, 4 ; 4 回繰り返す 00401D11 |> 8BB1 D0000000 /mov esi, dword ptr ds:[ecx+D0] ; esi = 固有 ID 00401D17 |. 8B38 |mov edi, dword ptr ds:[eax] ; edi = セットした値 00401D19 |. 33FE |xor edi, esi ; 固有ID で xor 00401D1B |. 8938 |mov dword ptr ds:[eax], edi ; xor した値を格納 00401D1D |. 83C0 04 |add eax, 4 ; 4 バイト進める 00401D20 |. 4A |dec edx 00401D21 |.^75 EE \jnz short crackme_.00401D11 00401D23 |. 5F pop edi 00401D24 |. 5E pop esi 00401D25 \. C3 retn ここでは固定値に対し固有 ID で xor しています。ということは 正当パスから得たハッシュ値 xor 固有 ID = 入力パスから得たハッシュ値 xor 固有 ID ということになります。 両辺に "xor 固有 ID" が入っているので固有 ID の存在は無視できます。 これは入力パス "98765432" から得た xor 前のハッシュ値です。 0012F8D8 0F 6B C7 BA 3B 7E 74 EB 9C 26 11 DE 7B C9 67 F3 ................ 以下は固有ID で xor する前のハッシュ値です。 0012FEF8 09 8A 13 7F 0E 25 9B 16 81 37 CB 9D 78 73 90 40 ................ 入力パスから上記のようなハッシュ値が生成されれば OK なのですが、 これはスパコンを駆使しない限り導き出せないところですが、正解パスは3文字とのこと。 頑張ってパス出ししてみて下さい。 総当たりで十分出せると思います。 自作するなり、既存のクラックツールを使うなり、当てずっぽうで試すなりして 各自やってみてください。 「Wonderful!!You analyzed powerful protection!! 」と表示されれば正解です。 ということで、UK_crackme DIFFICULT の分析はここまでです。お疲れ様でしたm(_ _)m。